Android14 窗口显示过程分析1

10/30/2024

# 1. 回顾图层的概念

20240822111043

(图片来自 https://www.jianshu.com/p/b0ef7c04486d)

在很多的图形相关的软件中都有图层的概念,那什么是图层呢?

简单的说,可以将每个图层理解为一张透明的纸,将图像的各部分绘制在不同的透明纸(图层)上。透过这层纸,可以看到纸后面的东西,而且每层纸都是独立的,无论在这层纸上如何涂画,都不会影响到其他图层中的图像。也就是说,每个图层可以独立编辑或修改,最后将透明纸叠加起来,从上向下俯瞰,即可得到并实时改变最终的合成效果。

图层,在 App 里面叫 Window/窗口,在 WMS 叫 SurfaceControl/Surface,在 SurfaceFlinger 叫 Layer

# 2. 添加窗口的两种方式

在 Android 中添加窗口主要有两种方式。

第一种,启动一个 Activity,代码如下:

// 启动 Activity
ComponentName cn = new ComponentName("当前Activity的全类名","启动Activity的全类名") ;
Intent intent = new Intent();
intent.setComponent(cn);
startActivity(intent);

// 还有很多其他方式启动 Activity,如果做过 App,应该非常熟悉,就不在列举了
1
2
3
4
5
6
7

第二种,添加悬浮窗(需要悬浮窗的运行时权限):

        private WindowManager mWindowManager;
        private WindowManager.LayoutParams mLayoutParams;

        // 准备工作
        // 取得系统窗体
        mWindowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);

        // 窗体的布局样式
        mLayoutParams = new WindowManager.LayoutParams();

        // 设置窗体显示类型——TYPE_APPLICATION_OVERLAY
        mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

        // 设置显示的模式
        mLayoutParams.format = PixelFormat.RGBA_8888;

        // 设置对齐的方法
        mLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;

        // 设置窗体宽度和高度
        mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;

        mLayoutParams.x = 550;
        mLayoutParams.y = 550;

        //窗口设置动画
        mLayoutParams.windowAnimations = R.style.MyWindow;
        //设置窗口名字
        mLayoutParams.setTitle("test-window");



        // 显示/隐藏窗口

        //一般, mView 是一个自定义 View;
        XxxView mView;
        // 显示悬浮窗
        mWindowManager.addView(mView, mLayoutParams);
        // 隐藏悬浮窗
        mWindowManager.removeView(mView);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

通过 Activity 显示一个窗口,封装的比较多,不太能看出具体的 API。悬浮窗的显示过程更为直接,直接使用了 Framework 为 App 提供的显示窗口的接口 WindowManager.addView;

        private WindowManager mWindowManager;
        
        private WindowManager.LayoutParams mLayoutParams;


        mWindowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
1
2
3
4
5
6

WindowManager 实际是一个 interface,getSystemService 返回的是实现类 WindowManagerImpl。

WindowManagerImpl.addView 的实现如下:

public final class WindowManagerImpl implements WindowManager {
    
    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

    //......

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyTokens(params);
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }

    // ......
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

这里会把添加窗口的任务通过 WindowManagerImpl 交给 WindowManagerGlobal.addView 来完成。

在 Activity 启动部分的分析中,我们知道:冷启动 Activity,目标 App 启动后,会去执行 LaunchActivityItem 和 ResumeActivityItem 这 2 个事务,这两个事务的执行过程中,会去添加显示窗口。

事务执行过程中的主要调用链如下:

LaunchActivityItem::execute
    ActivityThread::handleLaunchActivity
        ActivityThread::performLaunchActivity
            Instrumentation::newActivity                    -- 构建 Activity
            Activity::attach                                -- 构建 Window
                Window::init
                Window::setWindowManager
            Instrumentation::callActivityOnCreate  
                Activity::performCreate
                    Activity::onCreate
                        Activity::setContentView            -- 构建 DecorView

ResumeActivityItem::execute
    ActivityThread::handleResumeActivity
        ActivityThread::performResumeActivity   
            Activity::performResume   
                Instrumentation::callActivityOnResume
                    Activity::onResume        
        WindowManagerImpl::addView                          -- 构建 ViewRootImpl
            WindowManagerGlobal::addView                    -- 重要
                ViewRootImpl::setView                       -- 显示 Window
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

这里会把添加窗口的任务交给 WindowManagerGlobal.addView 来完成。

小结:无论是悬浮窗还是 Activity 最终都会通过 WindowManagerImpl 调用到 WindowManagerGlobal.addView 来添加窗口。

其中 WindowManagerGlobal.addView 中核心的三个元素:

  • View:窗口对应的 View 树
  • ViewGroup.LayoutParams params:窗口的大小位置布局信息
  • ViewRootImpl:App 中窗口显示的大管家,会在 WindowManagerGlobal.addView 中构建,和窗口一一对应。

20241211160101

# 3. 回顾 SurfaceFlinger 示例程序

Android 显示系统概述 (opens new window) 中,我们给出一个最简单的显示窗口的 Demo,对于显示一个窗口最核心的有以下 3 点:

  1. SurfaceControl,代表一个窗口
  2. SurfaceControl.Transaction,用于配置窗口参数的事务对象
  3. BBQ + GraphicBuffer + 渲染函数:分配缓存加渲染图形

App + WMS 实际就是在这个 Demo 的基础上的封装,封装的目的就是简化 App 的开发。

后续源码阅读,注意以下下几个重点:

  • 图层的建立过程,对应 SurfaceControl 的构建
  • 图层的配置过程,对应 SurfaceControl.Transaction 的构建和 apply 方法执行
  • 图层的绘制/渲染过程,对应 BBQ + GraphicBuffer + 渲染函数

# 4. Activity 显示过程整体分析

接下来,我们通过 Activity 的显示过程来分析窗口的显示过程。

需要注意的是 Activity 启动过程中会添加 StartingWindow 和 Activity 页面两个窗口,目前我们主要关心后者。

冷启动 Activity,目标 App 启动后,会去执行 LaunchActivityItem 和 ResumeActivityItem 这 2 个事务,整体的调用链如下:

LaunchActivityItem::execute
    ActivityThread::handleLaunchActivity
        ActivityThread::performLaunchActivity
            Instrumentation::newActivity                    -- 构建 Activity
            Activity::attach                                -- 构建 PhoneWindow
                Window::init
                Window::setWindowManager
            Instrumentation::callActivityOnCreate  
                Activity::performCreate
                    Activity::onCreate
                        Activity::setContentView            -- 构建 DecorView

ResumeActivityItem::execute
    ActivityThread::handleResumeActivity
        ActivityThread::performResumeActivity   
            Activity::performResume   
                Instrumentation::callActivityOnResume
                    Activity::onResume        
        WindowManagerImpl::addView                          -- 构建 ViewRootImpl
            WindowManagerGlobal::addView   
                ViewRootImpl::setView                          -- 显示 Window
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

这里额外说一下,很多 App 开发同学会认为 Activity 的 onResume 生命周期回调执行完成后,Activity 就会显示在显示屏上了,实际上,WindowManagerImpl::addView 执行完成后,Activity 才会显示到屏幕上了。而 WindowManagerImpl::addView 的执行是在 onResume 完成之后,所以这个普遍的观点其实是错误的。

ViewRootImpl::setView 是显示 Activity 的起点,是我们关注的重点,其主要调用链如下:

ViewRootImpl::setView
   ViewRootImpl::requestLayout
      ViewRootImpl::scheduleTraversals             
            ViewRootImpl.TraversalRunnable::run             -- 异步操作 
               ViewRootImpl::doTraversal
                  ViewRootImpl::performTraversals
                    ViewRootImpl::measureHierarchy                  --3 步预 measure ViewViewRootImpl::relayoutWindow                    --4 步:relayoutWindow,添加 SurfaceControl/Layer,测量窗口大小,初始化 BBQ
                        Session::relayout                           -- 远程调用,构建 SurfaceControl,测量窗口大小,Transaction 配置 Layer
                        ViewRootImpl::updateBlastSurfaceIfNeeded    -- 初始化 BBQ              
                    ViewRootImpl::performMeasure                    --5 步:View 的 测量布局绘制  
                    ViewRootImpl::performLayout
                    ViewRootImpl::performDraw        
                    ViewRootImpl::createSyncIfNeeded                --6 步:通知 WMS,客户端已经完成绘制,可以显示 SurfaceSession.addToDisplayAsUser                           ---1 步:addWindow
   mWindowLayout.computeFrames                          ---2 步:预计算 Window 尺寸
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

整体上我们可以把 Activity 的显示过程划分为 5 个阶段:

  • 阶段一:Activity Window DecorView 的初始化
  • 阶段二:向 WMS 添加 Window,预测量 Window 尺寸
  • 阶段三:预测量 View 树,添加 SurfaceControl/Layer,测量 Window 大小,初始化 BLASTBufferQueue
  • 阶段四:View 树的测量布局绘制
  • 阶段五:显示窗口

整体的流程如下图所示:

20241030161506

平台原因可能看不清,原图链接:https://boardmix.cn/app/share/CAE.CMLx1wwgASoQWXg0ZiHwaeFHfqsFNUL7lDAGQAE/hxtUlY,

# 4. 阶段一:Activity Window DecorView 的初始化过程分析

相关类类图如下:

20240908182911

成员之间的关系图如下:

20240916100403

这部分我主要还是关心三个对象:

  • 窗口的根 View,DecorView
  • 布局参数 LayoutParam
  • ViewRootImpl

Activity Window DecorView 在 LaunchActivityItem 事务执行的过程中,会被构建与初始化。

我们从 LaunchActivityItem::execute 方法入手分析:

// /frameworks/base/core/java/android/app/servertransaction/LaunchActivityItem.java

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
        // 构建 ActivityClientRecord 对象,保存了 Activity 方方面面的信息
        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
                client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
                mTaskFragmentToken);
        // 继续调用 handleLaunchActivity
        client.handleLaunchActivity(r, pendingActions, mDeviceId, null /* customIntent */);
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 构建 ActivityClientRecord 对象,该对象用于在 App 端记录 Activity 相关的信息
  • 继续调用 client::handleLaunchActivity 处理 Activity 的显示过程

这里的 client 的实际类型是 ActivityThread(ActivityThread 继承自 ClientTransactionHandler),接着会执行到 ActivityThread::handleLaunchActivity 方法:

// # ActivityThread

    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, int deviceId, Intent customIntent) {
        
        // ......

        // 主要关注这里
        final Activity a = performLaunchActivity(r, customIntent);


        // ......

        return a;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

我们主要关注到 performLaunchActivity 方法:

    // /frameworks/base/core/java/android/app/ActivityThread.java
    // # ActivityThread

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, mCompatibilityInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

        ContextImpl appContext = createBaseContextForActivity(r);

        // 通过反射构造目标 Activity
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
                    appContext.getAttributionSource());
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }


        // 通过反射构建 Application,回调 Application 生命周期回调 onCreate
        try {
            Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation);

            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
            if (localLOGV) Slog.v(
                    TAG, r + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + r.packageInfo.getPackageName()
                    + ", comp=" + r.intent.getComponent().toShortString()
                    + ", dir=" + r.packageInfo.getAppDir());

            // updatePendingActivityConfiguration() reads from mActivities to update
            // ActivityClientRecord which runs in a different thread. Protect modifications to
            // mActivities to avoid race.
            synchronized (mResourcesManager) {
                mActivities.put(r.token, r);
            }

            if (activity != null) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config =
                        new Configuration(mConfigurationController.getCompatConfiguration());
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                
                // window 目前为空
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }

                // Activity resources must be initialized with the same loaders as the
                // application context.
                appContext.getResources().addLoaders(
                        app.getResources().getLoaders().toArray(new ResourcesLoader[0]));

                appContext.setOuterContext(activity);

                // 注册 Activity,关键
                activity.attach(appContext, this, getInstrumentation(), r.token·,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
                        r.assistToken, r.shareableActivityToken);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                checkAndBlockForNetworkAccess();
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                if (r.mActivityOptions != null) {
                    activity.mPendingOptions = r.mActivityOptions;
                    r.mActivityOptions = null;
                }
                activity.mLaunchedFromBubble = r.mLaunchedFromBubble;
                activity.mCalled = false;
                // Assigning the activity to the record before calling onCreate() allows
                // ActivityThread#getActivity() lookup for the callbacks triggered from
                // ActivityLifecycleCallbacks#onActivityCreated() or
                // ActivityLifecycleCallback#onActivityPostCreated().
                r.activity = activity;
                // Activity 生命周期回调 onCreate
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                r.mLastReportedWindowingMode = config.windowConfiguration.getWindowingMode();
            }
            r.setState(ON_CREATE);

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }

        return activity;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  • 通过反射构造目标 Activity
  • 通过反射构建 Application,回调 Application 生命周期回调 onCreate
  • activity.attach
  • 执行 activity oncreate 生命周期回调

我们主要关注 attach 方法的实现:

    // /frameworks/base/core/java/android/app/Activity.java

    private Window mWindow;
    private IBinder mToken;

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, /*window 为空*/ActivityConfigCallback activityConfigCallback, IBinder assistToken,
            IBinder shareableActivityToken) {

        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);
        mActivityInfo = info;

        // 初始化 Window
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(mWindowControllerCallback);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mAssistToken = assistToken;
        mShareableActivityToken = shareableActivityToken;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }

        // 初始化 WindowManager
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);
        mWindow.setPreferMinimalPostProcessing(
                (info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0);

        getAutofillClientController().onActivityAttached(application);
        setContentCaptureOptions(application.getContentCaptureOptions());
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  • 构建 Window,mWindow 被赋值为新构建的 PhoneWindow
  • 初始化 WindowManager

WindowManager 是 App 与 WMS 通讯的辅助类,具体的实现类是 WindowManagerImpl。

    // /frameworks/base/core/java/android/view/Window.java
    // # Window

    private WindowManager mWindowManager;

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated;
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
    }
1
2
3

可以看出这里 new 了一个 WindowManagerImpl 并赋值给了 Window 的 mWindowManager 成员。

WindowManagerImpl 的实现如下:

public final class WindowManagerImpl implements WindowManager {

    // 重要成员
    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    
    // ......

    private WindowManagerImpl(Context context, Window parentWindow,
            @Nullable IBinder windowContextToken) {

        mContext = context;
        mParentWindow = parentWindow;
        mWindowContextToken = windowContextToken;
        mWindowMetricsController = new WindowMetricsController(mContext);
    }

    // ......
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

WindowManagerImpl 中还有一个重要成员 WindowManagerGlobal。

Activity 的 onCreate 生命周期回调,通常会调用到 setContentView 方法:

// /frameworks/base/core/java/android/app/Activity.java
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
1
2
3
4
5
// /frameworks/base/core/java/android/app/Activity.java
    public Window getWindow() {
        return mWindow;
    }
1
2
3
4

接着调用 PhoneWindow 的 setContentView 方法

// /frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
    
    private DecorView mDecor;
    ViewGroup mContentParent;
    private TextView mTitleView;

    @Override
    public void setContentView(int layoutResID) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
            installDecor(); //初始化好 mDecorView 以及两个子 View  mContentParent mTitleView
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

这里会调用 installDecor 方法初始化好 DecorView,也就是 Activity 的根 View。

接下来分析 ResumeActivityItem 事务的执行过程:

    // /frameworks/base/core/java/android/app/servertransaction/ResumeActivityItem.java
    public void execute(ClientTransactionHandler client, ActivityClientRecord r,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
        client.handleResumeActivity(r, true /* finalStateRequest */, mIsForward,
                mShouldSendCompatFakeFocus, "RESUME_ACTIVITY");
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }
1
2
3
4
5
6
7
8

接着调用 ActivityThread 的 handleResumeActivity 方法:

// #ActivityThread

    @Override
    public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
            boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        // skip below steps for double-resume and r.mFinish = true case.
        // 触发 onResume
        if (!performResumeActivity(r, finalStateRequest, reason)) {
            return;
        }
        if (mActivitiesToBeDestroyed.containsKey(r.token)) {
            // Although the activity is resumed, it is going to be destroyed. So the following
            // UI operations are unnecessary and also prevents exception because its token may
            // be gone that window manager cannot recognize it. All necessary cleanup actions
            // performed below will be done while handling destruction.
            return;
        }

        final Activity a = r.activity;

        if (localLOGV) {
            Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity
                    + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished);
        }

        final int forwardBit = isForward
                ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

        // If the window hasn't yet been added to the window manager,
        // and this guy didn't finish itself or start another activity,
        // then go ahead and add the window.
        boolean willBeVisible = !a.mStartedActivity;
        if (!willBeVisible) {
            willBeVisible = ActivityClient.getInstance().willActivityBeVisible(
                    a.getActivityToken());
        }
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);  // 不可见
            ViewManager wm = a.getWindowManager();
            // 重要,初始化布局参数
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            // window 的 type,代表是 app window
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //添加窗口
                    //实际添加的是 decorView
                    wm.addView(decor, l);
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    a.onWindowAttributesChanged(l);
                }
            }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
        } 

        // ......
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

关键的一点,构建好了布局参数 WindowManager.LayoutParams l,接着去调用 addView 添加窗口:

// /frameworks/base/core/java/android/view/WindowManagerImpl.java
    
    private IBinder mDefaultToken;

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyTokens(params); // 把 token 给到 params
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }

    private void applyTokens(@NonNull ViewGroup.LayoutParams params) {
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        assertWindowContextTypeMatches(wparams.type);
        // Only use the default token if we don't have a parent window and a token.
        if (mDefaultToken != null && mParentWindow == null && wparams.token == null) {
            // token 赋值
            wparams.token = mDefaultToken;
        }
        wparams.mWindowContextToken = mWindowContextToken;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

接着调用到 WindowManagerGlobal 的 addView 方法:

// /frameworks/base/core/java/android/view/WindowManagerGlobal.java

    // 应用内 View 集合
    @UnsupportedAppUsage
    private final ArrayList<View> mViews = new ArrayList<View>();
    @UnsupportedAppUsage
    // 应用内 ViewRootImpl 集合
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    @UnsupportedAppUsage
     // 应用内 View 布局参数集合
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();


    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            IWindowSession windowlessSession = null;
            // If there is a parent set, but we can't find it, it may be coming
            // from a SurfaceControlViewHost hierarchy.
            if (wparams.token != null && panelParentView == null) {
                for (int i = 0; i < mWindowlessRoots.size(); i++) {
                    ViewRootImpl maybeParent = mWindowlessRoots.get(i);
                    if (maybeParent.getWindowToken() == wparams.token) {
                        windowlessSession = maybeParent.getWindowSession();
                        break;
                    }
                }
            } 

            // 重要
            // ViewRootImpl
            if (windowlessSession == null) {
                root = new ViewRootImpl(view.getContext(), display);
            } else {
                root = new ViewRootImpl(view.getContext(), display,
                        windowlessSession, new WindowlessWindowLayout());
            }

            view.setLayoutParams(wparams);

            // 缓存起来
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                // 关键
                root.setView(view, wparams, panelParentView, userId);
            } catch (RuntimeException e) {
                final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
                // BadTokenException or InvalidDisplayException, clean up.
                if (viewIndex >= 0) {
                    removeViewLocked(viewIndex, true);
                }
                throw e;
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

构建好当前窗口对应的 ViewRootImpl,然后把 View LayoutParam ViewRootImpl 都保存在内部的容器中。然后去调用 root.setView 添加窗口。

至此,窗口显示的三个重要对象就构建好了:

  • 窗口的根 View,DecorView
  • 布局参数 LayoutParam
  • ViewRootImpl,在 WindowManagerGlobal.addView 中会 new 一个

# 参考资料